package com.wuxi.common.utils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.LoadingCache;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

@Slf4j
@Component
@RequiredArgsConstructor
public class CacheUtils {

    private final CacheManager cacheManager;

    /**
     * 获取缓存中的值
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @return 缓存值，如果不存在则返回null
     */
    public Object get(String cacheName, Object key) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            return null;
        }
        org.springframework.cache.Cache.ValueWrapper valueWrapper = cache.get(key);
        return valueWrapper != null ? valueWrapper.get() : null;
    }

    /**
     * 获取缓存中的值，并转换为指定类型
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param type 目标类型
     * @return 转换后的缓存值，如果不存在则返回null
     */
    public <T> T get(String cacheName, Object key, Class<T> type) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            log.info("{}缓存不存在!", cacheName);
            return null;
        }
        return cache.get(key, type);
    }

    /**
     * 获取缓存中的值，如果不存在则通过callable计算并缓存结果
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param callable 计算缓存值的回调函数
     * @return 缓存值或计算结果
     */
    public <T> T get(String cacheName, Object key, Callable<T> callable) throws Exception {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            return callable.call();
        }
        return cache.get(key, callable);
    }

    /**
     * 向缓存中存入值
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param value 缓存值
     */
    public void put(String cacheName, Object key, Object value) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache != null) {
            cache.put(key, value);
        }
    }

    /**
     * 从缓存中删除指定键的值
     * @param cacheName 缓存名称
     * @param key 缓存键
     */
    public void evict(String cacheName, Object key) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache != null) {
            cache.evict(key);
        }
    }

    /**
     * 清空指定名称的缓存
     * @param cacheName 缓存名称
     */
    public void clear(String cacheName) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache != null) {
            cache.clear();
        }
    }

    /**
     * 检查缓存中是否存在指定键
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @return 存在返回true，否则返回false
     */
    public boolean containsKey(String cacheName, Object key) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            return false;
        }
        return cache.get(key) != null;
    }

    /**
     * 获取泛型集合类型的缓存值（如 List<Authority>、List<User>）
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param typeReference 泛型类型引用（保留泛型信息）
     * @return 泛型集合对象，如果不存在则返回null
     */
    public <T> T getList(String cacheName, Object key, ParameterizedTypeReference<T> typeReference) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            return null;
        }
        org.springframework.cache.Cache.ValueWrapper valueWrapper = cache.get(key);
        if (valueWrapper == null) {
            return null;
        }
        return (T) valueWrapper.get();
    }

    /**
     * 获取泛型集合，如果不存在则通过callable计算并缓存
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param typeReference 泛型类型引用
     * @param callable 计算缓存值的回调函数
     * @return 泛型集合或计算结果
     */
    public <T> T getList(String cacheName, Object key, ParameterizedTypeReference<T> typeReference, Callable<T> callable) throws Exception {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            return callable.call();
        }
        T value = getList(cacheName, key, typeReference);
        if (value == null) {
            value = callable.call();
            cache.put(key, value);
        }
        return value;
    }

    /**
     * 获取Map类型缓存（键为K类型，值为V类型）
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param keyType Map的键类型
     * @param valueType Map的值类型
     * @return Map对象，若不存在则返回空Map（避免NullPointerException）
     */
    public <K, V> Map<K, V> getMap(String cacheName, Object key,
                                   Class<K> keyType, Class<V> valueType) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            log.warn("缓存[{}]不存在，返回空Map", cacheName);
            return Collections.emptyMap();
        }

        org.springframework.cache.Cache.ValueWrapper valueWrapper = cache.get(key);
        if (valueWrapper == null) {
            return Collections.emptyMap();
        }

        Object value = valueWrapper.get();
        if (!(value instanceof Map)) {
            log.error("缓存[{}]的键[{}]对应的值不是Map类型，实际类型：{}",
                    cacheName, key, value.getClass().getName());
            return Collections.emptyMap();
        }

        Map<?, ?> rawMap = (Map<?, ?>) value;
        Map<K, V> resultMap = new HashMap<>(rawMap.size());
        for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
            try {
                K mapKey = keyType.cast(entry.getKey());
                V mapValue = valueType.cast(entry.getValue());
                resultMap.put(mapKey, mapValue);
            } catch (ClassCastException e) {
                log.error("Map元素类型转换失败，键类型期望：{}，值类型期望：{}，实际键：{}，实际值：{}",
                        keyType.getName(), valueType.getName(),
                        entry.getKey().getClass().getName(),
                        entry.getValue().getClass().getName(), e);
            }
        }
        return resultMap;
    }

    /**
     * 获取Map类型缓存（支持复杂泛型值类型，如Map<String, List<User>>）
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param typeReference 泛型类型引用
     * @return Map对象，若不存在则返回空Map
     */
    public <K, V, M extends Map<K, V>> M getMap(String cacheName, Object key,
                                                ParameterizedTypeReference<M> typeReference) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            log.warn("缓存[{}]不存在，返回空Map", cacheName);
            return (M) Collections.emptyMap();
        }

        org.springframework.cache.Cache.ValueWrapper valueWrapper = cache.get(key);
        if (valueWrapper == null) {
            return (M) Collections.emptyMap();
        }

        Object value = valueWrapper.get();
        try {
            return (M) value;
        } catch (ClassCastException e) {
            log.error("缓存[{}]的键[{}]值不是期望的Map类型：{}",
                    cacheName, key, typeReference.getType(), e);
            return (M) Collections.emptyMap();
        }
    }

    /**
     * 获取Map缓存，若不存在则通过callable计算并缓存
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param keyType Map的键类型
     * @param valueType Map的值类型
     * @param callable 计算Map的回调函数
     * @return 缓存的Map或计算结果
     */
    public <K, V> Map<K, V> getMap(String cacheName, Object key,
                                   Class<K> keyType, Class<V> valueType,
                                   Callable<Map<K, V>> callable) throws Exception {
        Map<K, V> cacheMap = getMap(cacheName, key, keyType, valueType);
        if (!cacheMap.isEmpty()) {
            return cacheMap;
        }

        Map<K, V> newMap = callable.call();
        put(cacheName, key, newMap);
        return newMap;
    }

    /**
     * 向缓存存入Map（Caffeine适配版）
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param map 要缓存的Map
     */
    public <K, V> void putMap(String cacheName, Object key, Map<K, V> map) {
        if (map == null) {
            log.warn("存入缓存的Map为null，缓存名称：{}，键：{}", cacheName, key);
            return;
        }

        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            log.warn("缓存[{}]不存在，无法存入Map", cacheName);
            return;
        }

        cache.put(key, map);
    }

    /**
     * 获取Map中的某个键值
     * @param cacheName 缓存名称
     * @param key 缓存键
     * @param mapKey Map中的键
     * @param valueType Map值的类型
     * @return Map中对应的值，不存在则返回null
     */
    public <K, V> V getMapValue(String cacheName, Object key, K mapKey, Class<V> valueType) {
        Map<K, V> map = getMap(cacheName, key, null, null);
        if (map == null || map.isEmpty()) {
            return null;
        }

        Object value = map.get(mapKey);
        if (value == null) {
            return null;
        }

        try {
            return valueType.cast(value);
        } catch (ClassCastException e) {
            log.error("Map值类型转换失败，期望类型：{}，实际类型：{}",
                    valueType.getName(), value.getClass().getName(), e);
            return null;
        }
    }

    /**
     * 获取Caffeine缓存的配置信息（如大小、过期策略等）
     * @param cacheName 缓存名称
     * @return 缓存配置信息Map
     */
    public Map<String, Object> getCaffeineCacheConfig(String cacheName) {
        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            return Collections.emptyMap();
        }

        if (cache instanceof ConcurrentMapCache) {
            ConcurrentMapCache concurrentMapCache = (ConcurrentMapCache) cache;
            Object nativeCache = concurrentMapCache.getNativeCache();

            if (nativeCache instanceof Cache) {
                Cache<?, ?> caffeineCache = (Cache<?, ?>) nativeCache;

                try {
                    Field policyField = caffeineCache.getClass().getDeclaredField("policy");
                    policyField.setAccessible(true);
                    Object policy = policyField.get(caffeineCache);

                    Map<String, Object> config = new HashMap<>();
                    config.put("cacheName", cacheName);
                    config.put("estimatedSize", caffeineCache.estimatedSize());
                    config.put("isLoadingCache", caffeineCache instanceof LoadingCache);

                    if (policy != null) {
                        config.put("expirePolicy", policy.getClass().getSimpleName());
                    }
                    return config;
                } catch (Exception e) {
                    log.warn("获取Caffeine缓存配置失败", e);
                    return Collections.singletonMap("estimatedSize", caffeineCache.estimatedSize());
                }
            }
        }
        return Collections.emptyMap();
    }

    /**
     * 批量添加键值对到Map缓存
     * @param cacheName 缓存名称
     * @param key 缓存键（对应整个Map的键）
     * @param entries 要添加的键值对
     */
    @SafeVarargs
    public final <K, V> void putAllToMapCache(String cacheName, Object key, Map.Entry<K, V>... entries) {
        if (entries == null || entries.length == 0) {
            return;
        }

        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            log.warn("缓存[{}]不存在，无法批量添加Map条目", cacheName);
            return;
        }

        Map<K, V> map = getMap(cacheName, key, null, null);
        if (map == Collections.emptyMap()) {
            map = new HashMap<>();
        }

        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }

        cache.put(key, map);
    }

    /**
     * 从Map缓存中批量删除键
     * @param cacheName 缓存名称
     * @param key 缓存键（对应整个Map的键）
     * @param mapKeys 要删除的Map中的键
     */
    @SafeVarargs
    public final <K, V> void evictAllFromMapCache(String cacheName, Object key, K... mapKeys) {
        if (mapKeys == null || mapKeys.length == 0) {
            return;
        }

        org.springframework.cache.Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            log.warn("缓存[{}]不存在，无法批量删除Map条目", cacheName);
            return;
        }

        Map<K, V> map = getMap(cacheName, key, null, null);
        if (map.isEmpty()) {
            return;
        }

        for (K mapKey : mapKeys) {
            map.remove(mapKey);
        }

        cache.put(key, map);
    }
}
